using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Diagnostics;
using System.Xml;
using System.Xml.Schema; 
using XPathParser; 
using System.Xml.Linq; 
using System.Xml.XPath;

namespace CodeGenerationPipelineInterface
{
	[DebuggerDisplay("{ DebugInfo }")]
	public class Requirements : IXmlElement
	{
        private IXmlDocument document;
        private XNode node;
        private ManagedList<File> includeFiles;
        private ManagedList<Package> requiredPackages;
        private ManagedList<Import> requiredImports;
        private RequiredConfiguration requiredConfiguration;
        private const int unbounded = int.MaxValue;

		public Requirements(IXmlDocument document)
		{
           includeFiles = new ManagedList<File>("IncludeFiles", "Requirements.IncludeFiles", 0, unbounded);
           requiredPackages = new ManagedList<Package>("RequiredPackages", "Requirements.RequiredPackages", 0, unbounded);
           requiredImports = new ManagedList<Import>("RequiredImports", "Requirements.RequiredImports", 0, unbounded);
            
            this.document = document;
		}

        public IXmlDocument Document 
        { 
            get
            {
                return document;
            }

            set
            {
                document = value;
            }
        }

		[XPathAttribute("/ElementBuild/Requirements/IncludeFiles/File")]
		[MinOccursAttribute(0)]
		[MaxOccursAttribute(unbounded)]
 
		public ManagedList<File> IncludeFiles
		{
			get
			{
				return includeFiles;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/RequiredPackages/Package")]
		[MinOccursAttribute(0)]
		[MaxOccursAttribute(unbounded)]
 
		public ManagedList<Package> RequiredPackages
		{
			get
			{
				return requiredPackages;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/RequiredImports/Import")]
		[MinOccursAttribute(0)]
		[MaxOccursAttribute(unbounded)]
 
		public ManagedList<Import> RequiredImports
		{
			get
			{
				return requiredImports;
			}
		}

		[XPathAttribute("/ElementBuild/Requirements/RequiredConfiguration")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public RequiredConfiguration RequiredConfiguration
		{
			get
			{
				return requiredConfiguration;
			}
		}

		public string ElementName
		{
			get
			{
				return "Requirements";
			}
		}

		public string DebugInfo
		{
			get
			{
				return "Requirements";
			}
		}

        public XNode Node
        {
            get
            {
                return node;
            }
        }

        public IEnumerable<string> CDataElements
        {
            get
            {
                var element = (XElement)node;

                return element.Nodes().Where(n => n is XCData).Cast<XCData>().Select(c => c.Value);
            }
        }

        public void Read(XNode node)
        {
            var elementNode = (XElement) node;
            var prefix = elementNode.GetPrefixOfNamespace(elementNode.Name.Namespace);

            // IncludeFiles

            foreach (var elementFile in node.XPathSelectElements(prefix + ":IncludeFiles" + "/" + prefix + ":File", this.Document.NamespaceManager))
            {
                var includeFiles = new File(this.Document);

                includeFiles.Read(elementFile);

                this.includeFiles.Add(includeFiles);
            }

            // RequiredPackages

            foreach (var elementPackage in node.XPathSelectElements(prefix + ":RequiredPackages" + "/" + prefix + ":Package", this.Document.NamespaceManager))
            {
                var requiredPackages = new Package(this.Document);

                requiredPackages.Read(elementPackage);

                this.requiredPackages.Add(requiredPackages);
            }

            // RequiredImports

            foreach (var elementImport in node.XPathSelectElements(prefix + ":RequiredImports" + "/" + prefix + ":Import", this.Document.NamespaceManager))
            {
                var requiredImports = new Import(this.Document);

                requiredImports.Read(elementImport);

                this.requiredImports.Add(requiredImports);
            }

            // RequiredConfiguration

            var elementRequiredConfiguration = node.XPathSelectElement(prefix + ":RequiredConfiguration", this.Document.NamespaceManager);
            var requiredConfiguration = new RequiredConfiguration(this.Document);

            requiredConfiguration.Read(elementRequiredConfiguration);

            this.requiredConfiguration  = requiredConfiguration;

            this.node = node;
        }

        public void Write(XNode parentNode, XNamespace elementNamespace)
        {
            var parentElement = (XElement)parentNode;
            var element = new XElement(elementNamespace + this.ElementName);

            parentElement.Add(element);


            var includeFilesElement = new XElement(elementNamespace + "IncludeFiles");

            element.Add(includeFilesElement);

            foreach (var includeFiles in this.IncludeFiles)
            {
                includeFiles.Write(includeFilesElement, elementNamespace);
            }

            var requiredPackagesElement = new XElement(elementNamespace + "RequiredPackages");

            element.Add(requiredPackagesElement);

            foreach (var requiredPackages in this.RequiredPackages)
            {
                requiredPackages.Write(requiredPackagesElement, elementNamespace);
            }

            var requiredImportsElement = new XElement(elementNamespace + "RequiredImports");

            element.Add(requiredImportsElement);

            foreach (var requiredImports in this.RequiredImports)
            {
                requiredImports.Write(requiredImportsElement, elementNamespace);
            }

            var requiredConfiguration = this.RequiredConfiguration;

            requiredConfiguration.Write(element, elementNamespace);
        }

        public void Write(XDocument document, XmlNamespaceManager namespaces)
        {
            var namespaceAttributes = namespaces.Cast<string>().Where(s => !string.IsNullOrEmpty(s) && s != "xmlns" && s != "xml").Select(s => new XAttribute(XNamespace.Xmlns + s, XNamespace.Get(namespaces.LookupNamespace(s))));
            var element = new XElement(this.document.ElementNamespace + this.ElementName, namespaceAttributes.ToArray<object>());

            document.Add(element);

            var includeFilesElement = new XElement(this.Document.ElementNamespace + "IncludeFiles");

            element.Add(includeFilesElement);

            foreach (var includeFiles in this.IncludeFiles)
            {
                includeFiles.Write(includeFilesElement, this.Document.ElementNamespace);
            }

            var requiredPackagesElement = new XElement(this.Document.ElementNamespace + "RequiredPackages");

            element.Add(requiredPackagesElement);

            foreach (var requiredPackages in this.RequiredPackages)
            {
                requiredPackages.Write(requiredPackagesElement, this.Document.ElementNamespace);
            }

            var requiredImportsElement = new XElement(this.Document.ElementNamespace + "RequiredImports");

            element.Add(requiredImportsElement);

            foreach (var requiredImports in this.RequiredImports)
            {
                requiredImports.Write(requiredImportsElement, this.Document.ElementNamespace);
            }

            var requiredConfiguration = this.RequiredConfiguration;
            requiredConfiguration.Write(element, this.document.ElementNamespace);

        }

        public bool Validate(object sender, ValidationEventHandler handler)
        {
            var valid = true;
            foreach (File includeFiles in this.IncludeFiles)
            {
                includeFiles.Validate(sender, (sender2, e) =>
                {
                    handler(sender2, e);
                    valid = false;
                });
			}

            foreach (Package requiredPackages in this.RequiredPackages)
            {
                requiredPackages.Validate(sender, (sender2, e) =>
                {
                    handler(sender2, e);
                    valid = false;
                });
			}

            foreach (Import requiredImports in this.RequiredImports)
            {
                requiredImports.Validate(sender, (sender2, e) =>
                {
                    handler(sender2, e);
                    valid = false;
                });
			}

            RequiredConfiguration requiredConfiguration = this.RequiredConfiguration;
            requiredConfiguration.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            return valid;
        }
    }
}
